//
// Created by Tony on 2024/5/20.
//

#include "ExprParser.h"
#include "../common/Utils.h"
#include "../lexer/token/PunctToken.h"
#include "ast/leaf/Operator.h"
#include "ast/list/expr/BinaryExpr.h"
#include <memory>

namespace Parser {
using std::make_shared;

std::unordered_map<string, OpPrecedence> ExprParser::OP_TABLE;

void ExprParser::initOpPrecedence() {
    uint16_t value = 0;
    OpPrecedence precedence{};

    precedence = {++value, Assoc::RIGHT};
    OP_TABLE.emplace("=", precedence);

    precedence = {++value, Assoc::LEFT};
    OP_TABLE.emplace("||", precedence);

    precedence = {++value, Assoc::LEFT};
    OP_TABLE.emplace("&&", precedence);

    precedence = {++value, Assoc::LEFT};
    OP_TABLE.emplace("==", precedence);
    OP_TABLE.emplace("!=", precedence);

    precedence = {++value, Assoc::LEFT};
    OP_TABLE.emplace(">", precedence);
    OP_TABLE.emplace("<", precedence);
    OP_TABLE.emplace("<=", precedence);
    OP_TABLE.emplace(">=", precedence);

    precedence = {++value, Assoc::LEFT};
    OP_TABLE.emplace("+", precedence);
    OP_TABLE.emplace("-", precedence);

    precedence = {++value, Assoc::LEFT};
    OP_TABLE.emplace("*", precedence);
    OP_TABLE.emplace("/", precedence);
    OP_TABLE.emplace("%", precedence);

    precedence = {++value, Assoc::LEFT};
    OP_TABLE.emplace(".", precedence);
}

ExprParser::ExprParser() {
    if (OP_TABLE.empty()) {
        initOpPrecedence();
    }
}

shared_ptr<Ast> ExprParser::parse() { return expr(); }

bool ExprParser::isOperator(const shared_ptr<Lexer::Token>& token) {
    if (token == nullptr) {
        return false;
    }

    if (token->type() != Lexer::TokenType::PUNCT) {
        return false;
    }

    auto punctToken = static_pointer_cast<Lexer::PunctToken>(token);
    auto op = OP_TABLE.find(punctToken->getValue());
    return op != OP_TABLE.end();
}

/**
 * 根据下一个运算符的优先级判断当前运算符是个子表达式，还是运算数字
 * example: a + b * c, 返回true，代表右侧的b*c是一个子表达式，要先计算
 * example: a + b + c, 返回false，代表右侧b是个运算数，要先计算 a + b 表达式
 * example: a = b = c, 返回true，=号为右结合，因此第一个等号后面是个子表达式，要先计算赋值语句
 *                      b = c， 然后计算赋值语句 a = b
 * example: a.b = c,   返回false，"."运算符优先级大于"="号，因此b是一个运算数，要先计算表达式 a.b,
 *                      然后执行赋值
 *
 * @param curOp     当前运算符
 * @param nextOp    下一个运算符
 * @return boolean
 */
bool ExprParser::rightIsExpr(OpPrecedence curOp, OpPrecedence nextOp) {
    if (nextOp.assoc == Assoc::LEFT) {
        return curOp.value < nextOp.value;
    }
    return curOp.value <= nextOp.value;
}

shared_ptr<Ast> ExprParser::shiftRight(const shared_ptr<Ast>& left, OpPrecedence nextOpOfLeft) {
    auto token = tokenQueue.poll();
    if (!isOperator(token)) {
        setErrMsg("expect valid operator at %s", tokenQueue.getLastPosition().endStr().c_str());
        return nullptr;
    }

    auto op = make_shared<Operator>(token);
    auto right = factor();
    CHECK_NULL_RETURN(right, nullptr);

    while (true) {
        token = tokenQueue.peek();
        if (!isOperator(token)) {
            break;
        }

        string opName = static_pointer_cast<Lexer::PunctToken>(token)->getValue();
        auto nextOpOfRight = OP_TABLE[opName];
        if (!rightIsExpr(nextOpOfLeft, nextOpOfRight)) {
            break;
        }

        right = shiftRight(right, nextOpOfRight);
        CHECK_NULL_RETURN(right, nullptr);
    }

    return make_shared<BinaryExpr>(vector<shared_ptr<Ast>>{left, op, right});
}

/**
 * expr := factor [ op factor ]
 */
shared_ptr<Ast> ExprParser::expr() {
    auto expr = this->factor();
    CHECK_NULL_RETURN(expr, nullptr);

    while (true) {
        auto token = tokenQueue.peek();
        if (!isOperator(token)) {
            break;
        }

        string opName = static_pointer_cast<Lexer::PunctToken>(token)->getValue();
        auto nextOp = OP_TABLE[opName];

        expr = shiftRight(expr, nextOp);
        CHECK_NULL_RETURN(expr, nullptr);
    }

    return expr;
}

} // namespace Parser
